#ifndef SLICES_H_JM_20120815
#define SLICES_H_JM_20120815
/** @file Slices.h
 * Sada vseobecne pouzitelnych rezov pre generovanie spojeni textoveho modelu 
 * vypoctu pre bobox.
 *
 **/
//  ##
#include "SliceGenerator.h"
#include "AtomicGenerators.h"
//  ## C++
#include <cassert>

namespace jm4bbx {

	/**
	 * Generator rezu zabezpecujuci spojenie do kriza dvoch skupin boxov.
	 * Nech su to skupiny A a B (smer spojenia z A do B), tak kazdy box z A je 
	 * spojeny s kazdym boxom v B a boxy v ramci skupiny nie su spojene.
	 * Pocet boxov A (MERGER_FACTOR) je urceny poctom zliati do 1 boxu v B a 
	 * velkost skupiny B (FORK_FACOTR) je urcena poctom rozstiepeni 1 boxu 
	 * zo skupiny A. V trivialnych pripadoch su tieto pomenovanie zrejme. 
	 * Nech |A|=1 a |B|=n tak vygenerovany rez spojeni bude rozstiepenim sa 1 
	 * vypoctu na n.
	 * 
	 * @verbatim 
	 * Prikald pre |A|=1 a |B|=3
	 *            ___##
	 *           |   ##
	 *           |
	 *          ##___##
	 *          ##   ##
	 *           |
	 *           |___##
	 *               ##              
	 * @endverbatim
	 * 
	 * Opacne pre |A|=n a |B|=1 sa jedna o spojenie.
	 * 
	 * @verbatim 
	 * Prikald pre |A|=3 a |B|=1
	 *           ##____
	 *           ##    |
	 *                 |
	 *           ##____##
	 *           ##    ##
	 *                 |
	 *                 |
	 *           ##____|
	 *           ##  
	 * @endverbatim
	 * 
	 * Trieda umoznuje naviac konfigurovat pouzitie inkrementovania vstupnych ci
	 * vystupny indexov stlpcov s generovanim dalsieho spojenia. Je mozne teda
	 * zostrojit rez, ktory kazde spojenie smeruje na tie iste stlpce alebo 
	 * postupne s kazdym spojenim sa posunie na dalsi stlpec.
	 * 
	 * Trieda pouziva filter {@link filterVia}, ktorym je mozne niektore 
	 * spojenia nepouzit. Defaultna implemntacia pouzije vsetky spojenia 
	 * tak ako je ocakavane podla predoslej definicie chovania rezu. V pripade 
	 * filtrovania v podstate vznika iny rez a preto je moznost v potomkoch 
	 * CrossSlice metodu predefinovat a docielit tak filtrovanie zaradenia 
	 * spojeni.
	 **/
	class CrossSlice : public SliceGenerator {
	public:

		///**
		// * Zostrojenie nenakonfigurvaneho rezu. Chybaju nastavenia 
		// * identifikatorov pre zdrojove aj cielove boxy. 
		// * 
		// * @see setSrcBoxIds
		// * @see setDstBoxIds
		// * 
		// * @param types urcenie typu data tecucich spojeniami (aj s nasobnostou)
		// * @param incrementInArcs inkrementovat index vstupu s nasledujucim 
		// *			spojenim?
		// * @param incrementOutArcs inkrementovat index vystupu s nasledujucim 
		// *			spojenim?
		// **/
		//CrossSlice(int forkFactor, int mergeFactor, int srcArc = 0, 
		//	int dstArc = 0, const std::string& types = "",
		//	bool incrementInArcs = false, bool incrementOutArcs = false) {
		//	
		//		init(forkFactor, mergeFactor, srcArc, dstArc, types, 
		//			incrementInArcs, incrementOutArcs);
		//}

		/**
		 * Konstrukcia plne nakonfigurovaneho rezu, ktory moze hned generovat 
		 * textovu podobu rezu pre model vypoctu.
		 *
		 * @param srcBoxIds identifikatory zdrojovych boxov
		 * @param dstBoxIds identifikatory cilovych boxov
		 * @param forkFactor pocet rozstiepeni
		 * @param mergeFactor pocet zliati
		 * @param SRC_ARC pociatocny index vstupu
		 * @param DST_ARC pociatocny index vystupu 
		 * @param types urcenie typu data tecucich spojeniami (aj s nasobnostou)
		 * @param incrementInArcs inkrementovat index vstupu s nasledujucim 
		 *			spojenim?
		 * @param incrementOutArcs inkrementovat index vystupu s nasledujucim 
		 *			spojenim?
		 *			
		 * @tparam T1 typ nesuci identifikatory zdrojov - bud 1 id tj. int 
		 *				alebo  skupina id tj. kontajner poskytujuci operator[]
		 * @tparam T2 typ nesuci identifikatory cielov - bud 1 id tj. int alebo 
		 *				skupina id tj. kontajner poskytujuci operator
		 **/
		template<class T1, class T2>
		CrossSlice(T1 srcBoxIds, T2 dstBoxIds, int forkFactor, int mergeFactor, 
			int srcArc = 0, int dstArc = 0, const std::string& types = "",
			bool incrementInArcs = false, bool incrementOutArcs = false) {

				init(forkFactor, mergeFactor, srcArc, dstArc, types, 
					incrementInArcs, incrementOutArcs);

				setSrcBoxIds(srcBoxIds);
				setDstBoxIds(dstBoxIds);
		}

		/**
		 * Natstavenie identifikatorov zdrojovych boxov. Premazne predchadzajuce 
		 * nastavenia. Pocet identifikatorov musi byt dodrzany a je pevne  dany 
		 * ako parameter sablony celej tried - MERGE_FACTOR.
		 * 
		 * @param srcBoxIds identifikatory zdrojovych boxov
		 *
		 * @tparam T typ nesuci identifikatory zdrojov - bud 1 id tj. int alebo 
		 *				skupina id tj. kontajner poskytujuci operator[]
		 **/
		template<class T>
		void setSrcBoxIds(T srcBoxIds) {
			copyIds(srcBoxIds, srcBoxIds_, mergeFactor_);
		}

		/**
		 * Natstavenie identifikatorov cielovych boxov. Premazne predchadzajuce 
		 * nastavenia. Pocet identifikatorov musi byt dodrzany a je pevne  dany 
		 * ako parameter sablony celej tried - FORK_FACTOR.
		 * 
		 * @param dstBoxIds identifikatory cielovych boxov
		 *
		 * @tparam T typ nesuci identifikatory cielov - bud 1 id tj. int alebo 
		 *				skupina id tj. kontajner poskytujuci operator[]
		 **/
		template<class T>
		void setDstBoxIds(T dstBoxIds) {
			copyIds(dstBoxIds, dstBoxIds_, forkFactor_);
		}

		
		std::string generateBody(ViaIdGenerator& gen) const override {				
			std::stringstream stream;
			int incrementIn = 0;
			int incrementOut = 0;
			for (int mergeIndex = 0; mergeIndex < mergeFactor_; ++mergeIndex) {
				for (int forkIndex = 0; forkIndex < forkFactor_; ++forkIndex) {
					if (filterVia(forkIndex, mergeIndex)) {

						stream << 
							genVia(gen, srcBoxIds_[mergeIndex], 
							srcArc_ + incrementOut, dstBoxIds_[forkIndex], 
							dstArc_ + incrementIn, types_);

						if (incrementOutArcs_) { ++incrementOut; }
						if (incrementInArcs_) { ++incrementIn; }
					}
				}
			}
			return stream.str();
		}

		/// Zvysuje sa index stlpca vstupu pri kazdom vygenerovanom spojeni 
		/// v ramci rezu?
		bool isIncrementInArcs() const { return incrementInArcs_; }
		/// Nastav(true) - zakaz(false) inkrementaciu indexu stlpca  vstupu pri 
		/// kazdom vygenerovanom spojeni v ramci rezu.
		void setIncrementInArcs(bool val) { incrementInArcs_ = val; }
		/// Zvysuje sa index stlpca  vystupu pri kazdom vygenerovanom spojeni 
		/// v ramci rezu?
		bool isIncrementOutArcs() const { return incrementOutArcs_; }
		/// Nastav(true) - zakaz(false) inkrementaciu indexu stlpca  vystupu pri 
		/// kazdom vygenerovanom spojeni v ramci rezu.
		void setIncrementOutArcs(bool val) { incrementOutArcs_ = val; }

	protected:
		/// Inicializacia membrov.
		void init(int forkFactor, int mergeFactor, int srcArc, int dstArc, 
			const std::string& types, bool incrementInArcs, bool incrementOutArcs) {
				forkFactor_ = forkFactor;
				mergeFactor_ = mergeFactor;
				srcArc_ = srcArc;
				dstArc_ = dstArc;
				incrementInArcs_ = incrementInArcs;
				incrementOutArcs_ = incrementOutArcs;
				types_ = types;
				dstBoxIds_.resize(forkFactor_);
				srcBoxIds_.resize(mergeFactor_);
		}
		/**
		 * Filtrovanie zaradenia spojenia do rezu. V pripade zakladnej triedy 
		 * CrossSlice vracia vzdy true, pretoze sa generuju vsetky spojenia. 
		 * Potomkovia mozu tuto metodu predefinovat a docielit tak vyradenie 
		 * niektory neziaducich spojeni.
		 * 
		 * @warning Parametre su indexy boxov v ramci rezu a nie identifikatory 
		 *				v modele!
		 * 
		 * @param srcIndex index zdrojoveho boxu spojenia
		 * @param dstIndex index cieloveho boxu sedenia
		 **/
		virtual bool filterVia(int srcIndex, int dstIndex) const { 
			return true; 
		}

	private:
		/// Pocet boxov do ktorych sa ma rozstiepit vypocet.
		int forkFactor_;
		/// Pocet boxov z ktore sa zleju do jedneho vypoctu.
		int mergeFactor_;
		/// Index stlpca vstupnych dat.
		int srcArc_;
		/// Index stlpca vystupnych dat.
		int dstArc_;
		/// Pole identifikatorov zdrojovych boxov.
		std::vector<int> srcBoxIds_;
		/// Pole identifikatorov cielovych boxov.
		std::vector<int> dstBoxIds_;
		/// Inkrementovat indexy vstupnych stlpcov s generovanim dalsieho 
		/// spojenia?
		bool incrementInArcs_;
		/// Inkrementovat indexy vystupnych stlpcov s generovanim dalsieho 
		/// spojenia?
		bool incrementOutArcs_;
		/// Typ prenasanych dat skrz spojenia (aj s nasobnostou).
		std::string types_;		

		/**
		 * Skopirovanie hodnot identifikatorov kontajneru x do kontajneru y.
		 * @param x zdrojovy kontajner
		 * @param y cielovy kontajner
		 * @param size pocet kopirovanych prvkov
		 * 
		 * @tparam T1 typ zdrojoveho kontajneru - musi podporovat operator[] a 
		 *				niest kompatibilny typ hodnot s T2
		 * @tparam T2 typ cieloveho kontajneru - musi podporovat operator[] a 
		 *				niest kompatibilny typ hodnot s T1
		 **/
		template<class T1, class T2>
		void copyIds(T1 x, T2& y, size_t size) {
			for (size_t i = 0; i < size; ++i) {
				y[i] = x[i];
			}
		}

		/**
		 * Nastavenie hodnoty identifikatoru do kontajneru identifikatorov y.
		 * @param x id boxu
		 * @param y cielovy kontajner
		 * @param size pocet kopirovanych prvkov (vzdy musi byt 1)
		 * 
		 * @tparam T typ cieloveho kontajneru - musi podporovat operator[] a 
		 *				niest kompatibilny typ s typom int
		 **/
		template<class T>
		void copyIds(int x, T& y, size_t size) {
			assert(size == 1);
			y[0] = x;
		}
	};


	/**
	 * Generator rezu modelu vypoctu zabezpecujuci rozvetvenie (fork) vypoctu.
	 * Zdrojovy box (prave jeden) sa rozvetvi do FORK_FACTOR cielovych boxov.
	 * 
	 * @verbatim 
	 * Prikald pre FORK_FACTOR == 3
	 *            ___##
	 *           |   ##
	 *           |
	 *          ##___##
	 *          ##   ##
	 *           |
	 *           |___##
	 *               ##              
	 * @endverbatim
	 * 
	 **/
	class ForkSlice : public CrossSlice {
	public:
		/**
		 * @warning Pocet cielovy identifikatorov v dstBoxIds musi byt rovny 
		 *				hodnote FORK_FACTOR.
		 *				
		 * @param forBoxId identifikator zdrojoveho boxu vetvenia
		 * @param dstBoxIds identifikatory cilovych boxov
		 * @param forkFactor level rozvetvenia
		 * @param srcArc pociatosny index vstupu
		 * @param dstArc pociatocny index vystupu 
		 * @param types urcenie typu data tecucich spojeniami (aj s nasobnostou)
		 *
		 * @tparam T typ nesuci identifikatory cielov - v krajnom pripade 1 id
		 *			tj. int alebo skupina id tj. kontajner poskytujuci 
		 *			operator[]
		 * 
		 **/
		template<class T>
		ForkSlice(int forkBoxId, T dstBoxIds, int forkFactor, 
			int srcArc = 0, int dstArc = 0, const std::string& types = "")
			: CrossSlice(forkBoxId, dstBoxIds, forkFactor, 1 , srcArc, 	dstArc, 
			types, false, true) { }
	};

	/**
	 * Generator rezu modelu vypoctu zabezpecujuci spojenie (merge) vypoctu.
	 * Do cieloveho boxu (prave jeden) sa zleje MERGE_FACTOR zdrojovych boxov.
	 * 
	 * @verbatim 
	 * Prikald pre MERGE_FACTOR == 3
	 *           ##____
	 *           ##    |
	 *                 |
	 *           ##____##
	 *           ##    ##
	 *                 |
	 *                 |
	 *           ##____|
	 *           ##  
	 * @endverbatim
	 * 
	 **/
	class MergeSlice : public CrossSlice {
	public:
		/**
		 * @warning Pocet zdrojovych identifikatorov v srcBoxIds musi byt rovny 
		 *				hodnote MERGE_FACTOR.
		 *
		 * @param srcBoxIds identifikatory zdrojovych boxov
		 * @param mergeBoxId identifikator ciloveho boxu zlievania
		 * @param mergeFactor pocet zliati
		 * @param SRC_ARC pociatocny index vstupu
		 * @param DST_ARC pociatocny index vystupu 
		 * @param types urcenie typu data tecucich spojeniami (aj s nasobnostou)
		 *
		 * @tparam T typ nesuci identifikatory zdrojov - v krajnom pripade 1 id
		 *			tj. int alebo skupina id tj. kontajner poskytujuci 
		 *			operator[]
		 * 
		 **/
		template<class T>
		MergeSlice(T srcBoxIds, int mergedBoxId, int mergeFactor,
			int srcArc = 0, int dstArc = 0, const std::string& types = "")
			: CrossSlice(srcBoxIds, mergedBoxId, 1, mergeFactor, srcArc, dstArc, 
			types,true, false) { }
	};

	
	class ParallelSlice : public CrossSlice {
	public:
		/**
		 * @warning Pocet zdrojovych aj cielovych identifikatorov v srcBoxIds a 
		 *				dstBoxIds musi byt rovny  hodnote PARALLEL_FACTOR.
		 *
		 * @param srcBoxIds identifikatory zdrojovych boxov
		 * @param dstBoxIds identifikatory cielovych boxov
		 * @param parallelFactor pocet paralelnych vetiev
		 * @param types urcenie typu data tecucich spojeniami (aj s nasobnostou)
		 *
		 * @tparam T1 typ nesuci identifikatory zdrojov - v krajnom pripade 1 id
		 *			tj. int alebo skupina id tj. kontajner poskytujuci 
		 *			operator[]
		 * @tparam T2 typ nesuci identifikatory cielojov - v krajnom pripade 
		 *			1 id tj. int alebo skupina id tj. kontajner poskytujuci 
		 *			operator[]			
		 * 
		 **/
		template<class T1, class T2>
		ParallelSlice(T1 srcBoxIds, T2 dstBoxIds, int parallelFactor, 
			int srcArc = 0, int dstArc = 0, const std::string& types = "") 
			: CrossSlice(srcBoxIds, dstBoxIds, parallelFactor, parallelFactor, 
				srcArc, dstArc, types) { }

	protected:
		virtual bool filterVia(int forkIndex, int mergeIndex) const override { 
			return forkIndex == mergeIndex; 
		}
	};


	/**
	 * Generator rezu modelu vypoctu zabezpecujuci jednoduche spojenie 
	 * dvoch boxov.
	 * 
	 * @verbatim 
	 * Priklad jednoducheho spojenia dvoch boxov.
	 *           ##____##
	 *           ##    ##
	 * @endverbatim
	 * 
	 **/
	class PlainSlice : public ParallelSlice {
	public:
		PlainSlice(int srcBoxId, int dstBoxId, int srcArc = 0, int dstArc = 0, 
			const std::string& types = "") 
			: ParallelSlice(srcBoxId, dstBoxId, 1, srcArc, dstArc, types) { }
	};

} // namespace jm4bbx
#endif
